package server

import (
	"context"
	"errors"
	"fmt"
	"gitee.com/han-jiahao666/iam/internal/pkg/middleware"
	"github.com/gin-contrib/pprof"
	"github.com/gin-gonic/gin"
	"github.com/marmotedu/component-base/pkg/core"
	"github.com/marmotedu/component-base/pkg/version"
	"github.com/marmotedu/iam/pkg/log"
	ginprometheus "github.com/zsais/go-gin-prometheus"
	"golang.org/x/sync/errgroup"
	"net/http"
	"strings"
	"time"
)

// GenericAPIServer 包含 IAM API 服务器的状态。
// type GeneticAPIServer gin.Engine
type GenericAPIServer struct {
	middlewares         []string             // 中间件列表
	SecureServingInfo   *SecureServingInfo   // 保存 TLS 服务器的配置信息
	InsecureServingInfo *InsecureServingInfo // 保存非安全 HTTP 服务器的配置信息
	ShutdownTimeout     time.Duration        // 服务器关闭的超时时间。此超时时间指定了服务器优雅关闭返回之前的等待时间
	*gin.Engine
	healthz         bool // 是否启用健康检查
	enableMetrics   bool // 是否启用指标监控
	enableProfiling bool // 是否启用性能分析
	// wrapper for gin.Engine

	insecureServer, secureServer *http.Server // 非安全和安全的 HTTP 服务器实例
}

// initGenericAPIServer 对 GenericAPIServer 进行初始化设置。
func initGenericAPIServer(s *GenericAPIServer) {
	// 进行一些初始化设置
	// s.GET(path, ginSwagger.WrapHandler(swaggerFiles.Handler))

	s.Setup()              // 进行 Gin 引擎的设置
	s.InstallMiddlewares() // 安装中间件
	s.InstallAPIs()        // 安装 API 路由
}

// Setup 为 Gin 引擎进行一些设置工作。
func (s *GenericAPIServer) Setup() {
	// 重写 Gin 的 DebugPrintRouteFunc 函数，用于自定义路由信息的打印格式
	gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) {
		// 打印路由信息，包括 HTTP 方法、绝对路径、处理程序名称和处理器数量
		log.Infof("%-6s %-s --> %s (%d handlers)", httpMethod, absolutePath, handlerName, nuHandlers)
	}
}

// InstallMiddlewares 安装通用的中间件。
func (s *GenericAPIServer) InstallMiddlewares() {
	// 安装必要的中间件
	s.Use(middleware.RequestID()) // 安装请求 ID 中间件
	s.Use(middleware.Context())   // 安装上下文中间件

	// 安装自定义中间件
	for _, m := range s.middlewares {
		// 从 middleware.Middlewares 中查找对应的中间件
		mw, ok := middleware.Middlewares[m]
		if !ok {
			// 如果未找到中间件，记录警告信息并继续下一个中间件的安装
			log.Warnf("can not find middleware: %s", m)
			continue
		}
		// 记录安装的中间件信息
		log.Infof("install middleware: %s", m)
		// 应用中间件到 Gin 引擎
		s.Use(mw)
	}
}

// InstallAPIs 安装通用的 API 路由。
func (s *GenericAPIServer) InstallAPIs() {
	// 安装健康检查处理程序
	if s.healthz {
		// 注册 /healthz 路由，用于健康检查
		s.GET("/healthz", func(c *gin.Context) {
			core.WriteResponse(c, nil, map[string]string{"status": "ok"})
		})
	}

	// 安装指标监控处理程序
	if s.enableMetrics {
		// 创建一个新的 Prometheus 实例，用于 Gin 框架的指标监控
		promethus := ginprometheus.NewPrometheus("gin")
		// 使用 Prometheus 中间件，将其应用于 Gin 引擎
		promethus.Use(s.Engine)
	}

	// 安装性能分析处理程序
	if s.enableProfiling {
		// 注册 pprof 中间件到 Gin 引擎，用于性能分析
		pprof.Register(s.Engine)
	}

	// 注册 /version 路由，用于获取版本信息
	s.GET("/version", func(c *gin.Context) {
		core.WriteResponse(c, nil, version.Get())
	})
}

/*
// preparedGenericAPIServer 是一个私有包装器，强制在调用 Run 之前先调用 PrepareRun()。
type preparedGenericAPIServer struct {
    *GenericAPIServer
}

func (s *GenericAPIServer) PrepareRun() preparedGenericAPIServer {
    return preparedGenericAPIServer{s}
}
*/

// Run 启动 HTTP 服务器。只有在初始监听端口失败时才会返回。
func (s *GenericAPIServer) Run() error {
	// 为了可扩展性，这里使用自定义的 HTTP 配置模式
	// 创建非安全的 HTTP 服务器实例
	s.insecureServer = &http.Server{
		Addr:    s.InsecureServingInfo.Address, // 监听地址
		Handler: s,                             // 处理程序
		// ReadTimeout:    10 * time.Second,
		// WriteTimeout:   10 * time.Second,
		// MaxHeaderBytes: 1 << 20,
	}

	// 为了可扩展性，这里使用自定义的 HTTP 配置模式
	// 创建安全的 HTTP 服务器实例
	s.secureServer = &http.Server{
		Addr:    s.SecureServingInfo.Address(), // 监听地址
		Handler: s,                             // 处理程序
		// ReadTimeout:    10 * time.Second,
		// WriteTimeout:   10 * time.Second,
		// MaxHeaderBytes: 1 << 20,
	}

	// 创建一个错误组，用于并发处理多个 goroutine 的错误
	var eg errgroup.Group

	// 在 goroutine 中初始化非安全的 HTTP 服务器，
	// 这样它不会阻塞下面的优雅关闭处理
	eg.Go(func() error {
		// 记录开始监听非安全 HTTP 地址的信息
		log.Infof("Start to listening the incoming requests on http address: %s", s.InsecureServingInfo.Address)
		// 启动非安全的 HTTP 服务器
		if err := s.insecureServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
			// 记录致命错误信息
			log.Fatal(err.Error())
			return err
		}
		// 记录非安全的 HTTP 服务器停止的信息
		log.Infof("Server on %s stopped", s.InsecureServingInfo.Address)
		return nil
	})

	// 在 goroutine 中初始化安全的 HTTP 服务器
	eg.Go(func() error {
		// 获取证书和密钥文件路径
		key, cert := s.SecureServingInfo.CertKey.KeyFile, s.SecureServingInfo.CertKey.CertFile
		// 如果证书或密钥文件路径为空，或者绑定端口为 0，则不启动安全的 HTTP 服务器
		if cert == "" || key == "" || s.SecureServingInfo.BindPort == 0 {
			return nil
		}
		// 记录开始监听安全 HTTP 地址的信息
		log.Infof("Start to listening the incoming requests on https address: %s", s.SecureServingInfo.Address())
		// 启动安全的 HTTP 服务器
		if err := s.secureServer.ListenAndServeTLS(cert, key); err != nil && !errors.Is(err, http.ErrServerClosed) {
			// 记录致命错误信息
			log.Fatal(err.Error())
			return err
		}
		// 记录安全的 HTTP 服务器停止的信息
		log.Infof("Server on %s stopped", s.SecureServingInfo.Address())
		return nil
	})

	// 对服务器进行 Ping 操作，以确保路由器正常工作。
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	// 确保在函数结束时取消上下文
	defer cancel()
	// 如果启用了健康检查
	if s.healthz {
		// 调用 ping 函数进行 Ping 操作
		if err := s.ping(ctx); err != nil {
			return err
		}
	}

	// 等待所有 goroutine 完成，并处理可能的错误
	if err := eg.Wait(); err != nil {
		// 记录致命错误信息
		log.Fatal(err.Error())
	}

	return nil
}

// Close 优雅地关闭 API 服务器。
func (s *GenericAPIServer) Close() {
	// 创建一个带有 10 秒超时的上下文，用于通知服务器有 10 秒的时间来完成当前正在处理的请求
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	// 确保在函数结束时取消上下文
	defer cancel()

	// 关闭安全的 HTTP 服务器
	if err := s.secureServer.Shutdown(ctx); err != nil {
		// 记录关闭安全服务器失败的警告信息
		log.Warnf("Shutdown secure server failed: %s", err.Error())
	}

	// 关闭非安全的 HTTP 服务器
	if err := s.insecureServer.Shutdown(ctx); err != nil {
		// 记录关闭非安全服务器失败的警告信息
		log.Warnf("Shutdown insecure server failed: %s", err.Error())
	}
}

// ping 对 HTTP 服务器进行 Ping 操作，以确保路由器正常工作。
func (s *GenericAPIServer) ping(ctx context.Context) error {
	// 构造健康检查的 URL
	url := fmt.Sprintf("http://%s/healthz", s.InsecureServingInfo.Address)
	// 如果监听地址包含 0.0.0.0，则将 URL 改为使用 127.0.0.1
	if strings.Contains(s.InsecureServingInfo.Address, "0.0.0.0") {
		url = fmt.Sprintf("http://127.0.0.1:%s/healthz", strings.Split(s.InsecureServingInfo.Address, ":")[1])
	}

	// 循环进行 Ping 操作
	for {
		// 使用上下文创建一个新的 HTTP 请求
		req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
		if err != nil {
			return err
		}
		// 向 /healthz 发送 GET 请求来 Ping 服务器
		resp, err := http.DefaultClient.Do(req)
		// 如果请求成功且状态码为 200，表示路由器已成功部署
		if err == nil && resp.StatusCode == http.StatusOK {
			log.Info("The router has been deployed successfully.")
			// 关闭响应体
			resp.Body.Close()
			return nil
		}

		// 等待 1 秒后进行下一次 Ping 操作
		log.Info("Waiting for the router, retry in 1 second.")
		time.Sleep(1 * time.Second)

		// 检查上下文是否已超时
		select {
		case <-ctx.Done():
			// 如果超时，记录致命错误信息
			log.Fatal("can not ping http server within the specified time interval.")
		default:
		}
	}
	// return fmt.Errorf("the router has no response, or it might took too long to start up")
}
